package com.onlyxiahui.framework.json.validator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONReader;
import com.onlyxiahui.framework.json.validator.impl.*;

/**
 * 
 * date 2018-12-18 09:00:19<br>
 * description
 * 
 * @author XiaHui<br>
 * @since
 */
public class ValidatorService {

	Map<String, JSONObject> map = new HashMap<String, JSONObject>();
	Map<String, Validator<JSONObject, Object>> validatorMap = new HashMap<String, Validator<JSONObject, Object>>();

	public ValidatorService(String resourcePath) {
		initValidatorSettin(resourcePath);
		initValidator();
	}

	private void initValidatorSettin(String resourcePath) {
		try {
			PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			Resource[] resources = resolver.getResources(resourcePath);
			if (resources != null && resources.length > 0) {
				for (Resource resource : resources) {
					InputStream input = resource.getInputStream();
					BufferedReader in = new BufferedReader(new InputStreamReader(input));
					JSONReader reader = new JSONReader(in);
					try {

						reader.startArray();
						while (reader.hasNext()) {
							Object o = reader.readObject();
							if (o instanceof JSONObject) {
								JSONObject jo = (JSONObject) o;
								String name = jo.getString("name");
								JSONObject validator = jo.getJSONObject("validator");
								map.put(name, validator);
							}
						}
						reader.endArray();
					} finally {
						try {
							if (null != input) {
								input.close();
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
						try {
							if (null != in) {
								in.close();
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
						if (null != reader) {
							reader.close();
						}

					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void initValidator() {
		add(new JsonArrayValidator());
		add(new JsonBooleanValidator());
		add(new JsonEmailValidator());
		add(new JsonEqualsToValidator());
		add(new JsonInStringArrayValidator());
		add(new JsonIntegerValidator());
		add(new JsonMaxLengthValidator());
		add(new JsonMaxValidator());
		add(new JsonMinLengthValidator());
		add(new JsonMinValidator());
		add(new JsonNotBlankValidator());
		add(new JsonNotNullValidator());
		add(new JsonNumberValidator());

		add(new JsonRegexValidator());
		add(new JsonUrlValidator());
	}

	public void add(Validator<JSONObject, Object> validator) {
		validatorMap.put(validator.getKey(), validator);
	}

	public boolean hasValidate(String validateName) {
		return map.containsKey(validateName);
	}

	public List<ValidatorResult> validate(String json, String validateName) {
		JSONObject jsonObject = JSONObject.parseObject(json);
		return validate(jsonObject, validateName);
	}

	public List<ValidatorResult> validate(JSONObject jsonObject, String validateName) {
		List<ValidatorResult> list = new ArrayList<ValidatorResult>();
		JSONObject validatorJsonObject = map.get(validateName);
		if (null != validatorJsonObject) {
			Set<String> set = validatorJsonObject.keySet();
			for (String key : set) {
				JSONObject validatorInfo = validatorJsonObject.getJSONObject(key);
				Object value = jsonObject.get(key);
				validate(jsonObject, value, validatorInfo, list, key);
			}
		}
		return list;
	}

	private void validate(JSONObject data, Object attribute, JSONObject validatorInfo, List<ValidatorResult> list, String property) {

		String validatorsKey = "validators";
		String nodesKey = "nodes";

		String validatorKey = "validator";
		String messageKey = "message";
		String codeKey = "code";
		String valueKey = "value";

		boolean hasValidate = validatorInfo.containsKey(validatorsKey) && validatorInfo.get(validatorsKey) instanceof JSONArray;

		if (hasValidate) {
			JSONArray ja = validatorInfo.getJSONArray(validatorsKey);
			for (Object vo : ja) {
				if (vo instanceof JSONObject) {
					JSONObject v = (JSONObject) vo;
					String validatorName = v.getString(validatorKey);
					Validator<JSONObject, Object> validator = validatorMap.get(validatorName);
					if (null != validator) {
						String code = v.getString(codeKey);
						String message = v.getString(messageKey);
						Object value = v.get(valueKey);

						ValidatorResult vr = validator.valid(data, attribute, value, code, message);
						if (null != vr) {
							vr.setPropertyPath(property);
							list.add(vr);
						}
					}
				}
			}
		}

		boolean hasNodes = validatorInfo.containsKey(nodesKey) && validatorInfo.get(nodesKey) instanceof JSONObject;
		if (hasNodes) {
			JSONObject validateObject = validatorInfo.getJSONObject(nodesKey);
			if (null != attribute) {
				// boolean isNode =property.equals(validatorsKey);
				boolean validate = validateObject.containsKey(validatorsKey) && validateObject.get(validatorsKey) instanceof JSONArray;
				if (attribute instanceof JSONArray) {
					arrayValidate(data, (JSONArray) attribute, validateObject, list, property);
				} else if (attribute instanceof JSONObject && validate) {
					validate(data, attribute, validateObject, list, property);
				} else if (attribute instanceof JSONObject) {
					objectValidate(data, (JSONObject) attribute, validateObject, list, property);
				}
			}
		}
	}

	private void arrayValidate(JSONObject data, JSONArray array, JSONObject validatorInfo, List<ValidatorResult> list, String property) {
		int size = array.size();

		for (int i = 0; i < size; i++) {
			Object value = array.get(i);
			if (value instanceof JSONArray) {
				validate(data, value, validatorInfo, list, property + ".[" + i + "]");
			} else if (value instanceof JSONObject) {
				objectValidate(data, (JSONObject) value, validatorInfo, list, property + ".[" + i + "]");
			} else {
				validate(data, value, validatorInfo, list, property + ".[" + i + "]");
			}
		}
	}

	private void objectValidate(JSONObject data, JSONObject attribute, JSONObject validatorInfo, List<ValidatorResult> list, String property) {
		Set<String> set = validatorInfo.keySet();
		for (String key : set) {
			JSONObject vi = validatorInfo.getJSONObject(key);
			Object value = attribute.get(key);
			validate(data, value, vi, list, property + "." + key);
		}
	}
}
